package com.xzzz.irda.auth.server.authentication;

import com.xzzz.irda.auth.core.authorization.Authorization;
import com.xzzz.irda.auth.core.authorization.AuthorizationInner;
import com.xzzz.irda.auth.core.constants.GrantTypeEnum;
import com.xzzz.irda.auth.core.exception.AuthException;
import com.xzzz.irda.auth.core.exception.AuthRCode;
import com.xzzz.irda.auth.core.AuthBaseProperties;
import com.xzzz.irda.auth.core.util.TokenUtil;
import com.xzzz.irda.auth.server.AuthServerConfiguration;
import com.xzzz.irda.auth.server.pojo.LoginMetadata;
import com.xzzz.irda.auth.server.service.UserDetail;
import com.xzzz.irda.auth.server.service.UserDetailService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Optional;

/**
 * 内部登录,无密码模式,用于某些不需要密码的场景,某些第三方平台登录校验的token获取
 * 如微信登录,校验功能由微信校验,登录成功后则可以使用此功能获取token
 * <p>
 * AuthorizationInner
 *
 * @author wangyf
 * 
 * @since 1.1.0 增加 {@link AuthenticationManager#refreshAuthorization} 方法
 */
@Slf4j
public class AuthenticationInnerManager extends AbstractAuthenticationManager {

    @Autowired
    private UserDetailService userDetailService;


    /**
     * 用于构造抽象类中的授权配置和配置文件
     *
     * @param authServerConfiguration 服务端配置
     * @param authBaseProperties      配置文件
     */
    public AuthenticationInnerManager(
            AuthServerConfiguration authServerConfiguration,
            AuthBaseProperties authBaseProperties) {
        super(authServerConfiguration, authBaseProperties);
    }

    /**
     * 该类的授权类型, 策略类初始化时的类型判断
     *
     * @return 授权类型
     * @see AuthenticationManagerStrategy#AuthenticationManagerStrategy(List)
     */
    @Override
    public String getGrantType() {
        return GrantTypeEnum.INNER.getType();
    }

    /**
     * 刷新token信息
     *
     * @param token 授权主体的Key
     */
    @Override
    public void refreshAuthorization(String token) {
        Authorization authorization = this.getAuthorization(token);
        Long userId = authorization.getAccessToken().getUserId();
        // 获取用户的有效token
        String oldUniqueToken = TokenUtil.buildUniqueTokenKey(authorization);
        // 获取用户的最新信息
        UserDetail userDetail = userDetailService.loadUserByUserId(userId);
        // 设置信息
        authorization.getAccessToken().setMetadata(userDetail.getMetadata());
        // 保存最新的token
        this.saveAuthorization(authorization);

        String newUniqueToken = TokenUtil.buildUniqueTokenKey(authorization);

        /*
         * 如果两次的有效 token 发生了变化, 则说明该 token 的 [userId,ClientId] 中的某一项发生了变化,
         * 为了保证 effective_token 中没有脏数据, 需要把旧的 effective_token 进行删除
         * 通常发生在用户的 app_id 与 token 生成之后的场景
         *
         * 如:
         *  店铺注册时, 会颁发一个授权给用户, 此时授权中的 appid 为空, 因为还无法确认店铺会使用哪一个 app
         *  在店主填写邀请码之后, 店铺即会绑定一个 app, 此时为了避免重新获取授权的情况, 需要刷新授权中的信息,
         *  那么对应 effective_token 的 key 则会发生变化, 旧的 effective_token 就要进行删除
         */
        if (!oldUniqueToken.equals(newUniqueToken)) {
            log.debug("删除旧的 effective_token:{}", oldUniqueToken);
            getAuthRepo().removeUniqueToken(oldUniqueToken);
        }
    }

    /**
     * 授权对象的业务数据补充
     *
     * @param authorization 授权对象
     * @param login         请求参数
     * @return 授权主体
     */
    @Override
    public Authorization authorizationBeforeProcess(Authorization authorization, LoginMetadata login) {
        AuthorizationInner authorizationInner = (AuthorizationInner) authorization;
        if (login.getUserId() == null) {
            throw new AuthException(AuthRCode.USER_NOT_EXIST);
        }
        authorizationInner.setUserId(login.getUserId());
        return authorizationInner;
    }

    /**
     * 处理内部登录方式的授权主体, 主体中包含用户名
     * <p>
     * 生成 token, 不同的授权类型获取用户的方式不同, 此处需要重写 token 生成方式
     *
     * @param authorization 授权主体
     * @return 返回 token
     */
    @Override
    public UserDetail findUser(Authorization authorization, LoginMetadata loginMetadata) {
        AuthorizationInner authorizationInner = (AuthorizationInner) authorization;

        // 获取用户信息并检查
        UserDetail userDetail = userDetailService.loadUserByUserId(authorizationInner.getUserId());
        Optional.ofNullable(userDetail).orElseThrow(() -> {
            log.error("[AUTHORIZ] 未获取到授权用户的信息 > userid:{}", authorizationInner.getUserId());
            return new AuthException(AuthRCode.USER_NOT_EXIST);
        });
        return userDetail;
    }

}
